import 'dart:math';

import 'package:flutter/material.dart';

import '../../../utils/ex/kq_ex.dart';
import '../base/base_chart.dart';
import '../ex/extension.dart';
import 'base/base_pie_entity.dart';
import 'base/base_pie_chart.dart';

/// 饼状图
class KqPieChartDelegate extends BasePieChartDelegate<List<PieChartEntity>> {
  /// 构建饼状图
  ///
  /// [innerRadius]为内圆半径，此值>0则绘制图形为环形，=0则为扇形。[sliceSpace]为各扇形直接的间隙。
  /// [baseMapColor]为饼状图背景颜色，如果数据为空的且未设置[emptyWidgetBuilder],则会在界面看到一个
  /// 以[baseMapColor]为背景颜色的圆(innerRadius == 0)或圆环(innerRadius>0)。[drawStartAngle]为
  /// 饼状图起始绘制角度。[labelPosition]指定饼状图标签绘制的位置。[emptyWidgetBuilder]为无数据时构建
  /// 需要显示的内容。
  KqPieChartDelegate(
      {super.animDuration,
      required super.radius,
      super.data,
      super.innerRadius,
      super.sliceSpace,
      super.baseMapColor,
      super.drawStartAngle,
      super.labelPosition,
      PieChartGestureHandler? gestureHandler,
      PieChartDataRender? dataRender,
      PieChartLabelRender? labelRender,
      super.emptyWidgetBuilder,
      super.isDataEmpty})
      : super(
            dataRender: dataRender ?? const PieChartDataRender(),
            labelRender: labelRender,
            gestureHandler: gestureHandler);

  /// 构建饼状图，部分非必传属性如果未设置则使用默认值
  ///
  /// [innerRadius]为内圆半径，此值>0则绘制图形为环形，=0则为扇形。[sliceSpace]为各扇形直接的间隙。
  /// [baseMapColor]为饼状图背景颜色，如果数据为空的且未设置[emptyWidgetBuilder],则会在界面看到一个
  /// 以[baseMapColor]为背景颜色的圆(innerRadius == 0)或圆环(innerRadius>0)。[drawStartAngle]为
  /// 饼状图起始绘制角度。[labelPosition]指定饼状图标签绘制的位置。[emptyWidgetBuilder]为无数据时构建
  /// 需要显示的内容。
  KqPieChartDelegate.withDefault(
      {Duration? animDuration,
      required double radius,
      List<PieChartEntity>? data,
      double innerRadius = 0,
      double sliceSpace = 0,
      Color baseMapColor = Colors.transparent,
      double drawStartAngle = 0,
      LabelPosition? labelPosition,
      PieChartGestureHandler? gestureHandler,
      PieChartDataRender? dataRender,
      PieChartLabelRender? labelRender,
      Widget Function()? emptyWidgetBuilder,
      bool Function(List<PieChartEntity>? data)? isDataEmpty})
      : this(
            animDuration: animDuration,
            radius: radius,
            data: data,
            innerRadius: innerRadius,
            sliceSpace: sliceSpace,
            baseMapColor: baseMapColor,
            drawStartAngle: drawStartAngle,
            labelPosition: labelPosition,
            dataRender: dataRender,
            labelRender: labelRender ?? const PieChartLabelRender(),
            gestureHandler: gestureHandler ?? PieChartGestureHandler(),
            isDataEmpty: isDataEmpty,
            emptyWidgetBuilder: emptyWidgetBuilder ?? getDefEmptyView);

  @override
  bool get isEmptyData {
    var isDataEmptyFunc = isDataEmpty;
    if (isDataEmptyFunc != null) {
      return isDataEmptyFunc.call(data);
    }

    return data.isNullOrEmpty;
  }

  @override
  void didUpdateWidget(covariant BaseChart oldWidget) {
    super.didUpdateWidget(oldWidget);
    _updateGestureHandler(oldWidget);
  }

  void _updateGestureHandler(BaseChart oldWidget) {
    var gestureHandler = this.gestureHandler;
    if (gestureHandler == null || gestureHandler is! PieChartGestureHandler) {
      return;
    }

    var oldGestureHandler = oldWidget.delegate.gestureHandler;
    if (oldGestureHandler == null ||
        oldGestureHandler is! PieChartGestureHandler) {
      return;
    }

    gestureHandler._rotation = oldGestureHandler._rotation;
    gestureHandler._firstMoveAngle = oldGestureHandler._firstMoveAngle;
    gestureHandler._touchData = oldGestureHandler._touchData;
    gestureHandler._oldTouchData = oldGestureHandler._oldTouchData;
    gestureHandler._rotationDirection = oldGestureHandler._rotationDirection;
  }
}

/// 绘制饼状图扇形内容
class PieChartDataRender with BasePieDataRender<KqPieChartDelegate> {
  const PieChartDataRender({this.highLightRender});

  final PieChartHighLightRender? highLightRender;

  @override
  void onDraw(KqPieChartDelegate chart, Canvas canvas, double animProgress) {
    if (chart.isEmptyData) {
      drawBg(chart, canvas);
      return;
    }

    double totalValue = 0;
    var dataList = chart.data ?? [];
    for (var element in dataList) {
      totalValue += element.value;
    }

    PieChartGestureHandler? gestureHandler = chart.gestureHandler == null
        ? null
        : chart.gestureHandler as PieChartGestureHandler;

    PieChartEntity? touchData = gestureHandler?.touchData;
    PieChartHighLightRender? highLightRender = this.highLightRender;

    double rotationAngle =
        chart.drawStartAngle + (gestureHandler?.rotation ?? 0);
    // 偏移角度，也是当前需要绘制扇形的起始角度
    double offsetDegrees = rotationAngle;
    double sliceSpace = dataList.length == 1 ? 0 : chart.sliceSpace;
    for (int i = 0; i < dataList.length; i++) {
      PieChartEntity data = dataList[i];
      if (!data.isDraw) {
        continue;
      }

      double degrees =
          totalValue == 0 ? 0 : data.value / totalValue * 360 * animProgress;
      data.$drawStartAngle = offsetDegrees;
      data.$drawSweepAngle = degrees;

      if (touchData == data && highLightRender != null) {
        data.$drawRadius = highLightRender.getRadius(chart.radius, data);
        Offset offset = highLightRender.getOffset(data);
        data.$offset = offset;
        if (data.drawSweepAngle > 0) {
          highLightRender.onDraw(chart, canvas, offsetDegrees, degrees, data);
        }
      } else {
        data.$drawRadius = chart.radius + data.addedRadius;
        data.$offset = Offset.zero;
        if (data.drawSweepAngle > 0) {
          canvas.drawArcEx(
              center: Offset(chart.size.centerX, chart.size.centerY),
              radius: data.drawRadius,
              innerRadius: chart.innerRadius,
              startAngle: data.drawStartAngle,
              sweepAngle: data.drawSweepAngle,
              color: data.color,
              offset: data.offset,
              sliceSpace: sliceSpace);
        }
      }

      offsetDegrees += degrees;
    }
  }

  void drawBg(KqPieChartDelegate chart, Canvas canvas) {
    if (chart.baseMapColor.alpha == 0) {
      return;
    }

    canvas.drawArcEx(
        center: Offset(chart.size.centerX, chart.size.centerY),
        radius: chart.radius,
        innerRadius: chart.innerRadius,
        startAngle: 0,
        sweepAngle: 360,
        color: chart.baseMapColor);
  }
}

/// 绘制饼状图标签
class PieChartLabelRender with BasePieLabelRender<KqPieChartDelegate> {
  const PieChartLabelRender(
      {this.minShowLabelAngle, this.minShowLabelAngleByNear});

  /// 显示标签的饼图最小角度，小于此值的饼图不绘制标签，不设置则始终绘制。
  final double? minShowLabelAngle;

  /// 标签满足[minShowLabelAngle]条件且此值不为null，如果标签前后的饼图绘制角度大于此值，则依然绘制满足[minShowLabelAngle]条件的标签
  final double? minShowLabelAngleByNear;

  @override
  void onDraw(KqPieChartDelegate chart, Canvas canvas, double animProgress) {
    var dataList = chart.data;
    if (dataList.isNullOrEmpty) {
      return;
    }

    var labelPosition = chart.labelPosition;
    if (labelPosition is InSide) {
      drawInSideLabel(chart, canvas, labelPosition, dataList!);
    } else if (labelPosition is OutSide) {
      drawOutSideLabel(chart, canvas, labelPosition, dataList!);
    }
  }

  /// 当[LabelPosition]为[InSide]时，绘制显示在扇形内部的标签
  @protected
  void drawInSideLabel(KqPieChartDelegate chart, Canvas canvas,
      InSide labelPosition, List<PieChartEntity> dataList) {
    Offset center = chart.size.center(Offset.zero);
    var count = dataList.length;
    for (int i = 0; i < count; i++) {
      var value = dataList[i];
      if (!value.isDraw || !value.drawLabel) {
        continue;
      }

      if (minShowLabelAngle != null &&
          value.drawSweepAngle < minShowLabelAngle!) {
        if (minShowLabelAngleByNear == null) {
          continue;
        }

        var index = i + 1;
        if (index >= count) {
          index = 0;
        }
        if (dataList[index].drawSweepAngle < minShowLabelAngleByNear!) {
          continue;
        }

        index = i - 1;
        if (index < 0) {
          index = count - 1;
        }

        if (dataList[index].drawSweepAngle < minShowLabelAngleByNear!) {
          continue;
        }
      }

      double centerAngle = value.drawStartAngle + value.drawSweepAngle / 2;
      double radius2 = (value.drawRadius +
              sqrt(pow(value.offset.dx, 2) + pow(value.offset.dy, 2))) *
          labelPosition.offsetCenterPercent;
      double x1 = center.dx + cos(centerAngle.toRadian) * radius2;
      double y1 = center.dy + sin(centerAngle.toRadian) * radius2;
      String? label = value.label;

      TextPainter? textPaint;
      TextPainter? textPaint2;
      if (label.isNotNullOrEmpty) {
        textPaint = TextPainter(
            text: TextSpan(
                text: label,
                style: TextStyle(
                    color: value.labelColor, fontSize: value.labelSize)),
            maxLines: 1,
            textAlign: TextAlign.center,
            textDirection: TextDirection.ltr);
        textPaint.layout();
      }

      String? secondLabel = value.secondLabel;
      if (secondLabel.isNotNullOrEmpty) {
        textPaint2 = TextPainter(
            text: TextSpan(
                text: secondLabel,
                style: TextStyle(
                    color: value.secondLabelColor,
                    fontSize: value.secondLabelSize)),
            maxLines: 1,
            textAlign: TextAlign.center,
            textDirection: TextDirection.ltr);
        textPaint2.layout();
      }

      double labelHeight = textPaint?.height ?? 0;
      double secondLabelHeight = textPaint2?.height ?? 0;
      if (textPaint != null) {
        double height = labelHeight + secondLabelHeight;
        textPaint.paint(
            canvas, Offset(x1 - textPaint.width / 2, y1 - height / 2));
      }

      if (textPaint2 != null) {
        double height = labelHeight + secondLabelHeight;
        textPaint2.paint(canvas,
            Offset(x1 - textPaint2.width / 2, y1 - height / 2 + labelHeight));
      }
    }
  }

  /// 当[LabelPosition]为[OutSide]时，绘制显示在扇形外部的标签
  @protected
  void drawOutSideLabel(KqPieChartDelegate chart, Canvas canvas,
      OutSide labelPosition, List<PieChartEntity> dataList) {
    Offset center = chart.size.center(Offset.zero);
    var count = dataList.length;
    for (int i = 0; i < count; i++) {
      var value = dataList[i];
      if (!value.isDraw || !value.drawLabel) {
        continue;
      }

      if (minShowLabelAngle != null &&
          value.drawSweepAngle < minShowLabelAngle!) {
        if (minShowLabelAngleByNear == null) {
          continue;
        }

        var index = i + 1;
        if (index >= count) {
          index = 0;
        }
        if (dataList[index].drawSweepAngle < minShowLabelAngleByNear!) {
          continue;
        }

        index = i - 1;
        if (index < 0) {
          index = count - 1;
        }

        if (dataList[index].drawSweepAngle < minShowLabelAngleByNear!) {
          continue;
        }
      }

      IndicatorLine indicatorLine = labelPosition.lineFactory.call(value);
      double centerAngle =
          (value.drawStartAngle + value.drawSweepAngle / 2).to360Range;
      double centerRadian = centerAngle.toRadian;
      double radius2 = value.drawRadius +
          sqrt(pow(value.offset.dx, 2) + pow(value.offset.dy, 2)) +
          indicatorLine.offset;
      // 指示线第一段起始X坐标
      double x1;
      // 指示线第一段起始Y坐标
      double y1;

      if (indicatorLine.drawStartCircle && indicatorLine.circleRadius > 0) {
        // 绘制指示线起始端处的圆点
        radius2 += indicatorLine.circleRadius;
        x1 = center.dx + cos(centerRadian) * radius2;
        y1 = center.dy + sin(centerRadian) * radius2;
        Paint paint = Paint()
          ..style = PaintingStyle.fill
          ..color = indicatorLine.circleColor;
        canvas.drawCircle(Offset(x1, y1), indicatorLine.circleRadius, paint);
        radius2 += indicatorLine.circleRadius;
      }

      x1 = center.dx + cos(centerRadian) * radius2;
      y1 = center.dy + sin(centerRadian) * radius2;

      // 标签宽度
      double labelWidth;
      double labelHeight;
      TextPainter? labelPainter;
      if (value.label.isNullOrEmpty) {
        labelWidth = 0;
        labelHeight = 0;
      } else {
        labelPainter = TextPainter(
          text: TextSpan(
              text: value.label,
              style: TextStyle(
                  color: value.labelColor, fontSize: value.labelSize)),
          maxLines: 1,
          textAlign: TextAlign.center,
          textDirection: TextDirection.ltr,
        )..layout();

        labelWidth = labelPainter.width;
        labelHeight = labelPainter.height;
      }

      // 第二个标签宽度
      double secondLabelWidth;
      double secondLabelHeight;
      TextPainter? secondLabelPainter;
      if (value.secondLabel.isNullOrEmpty ||
          labelPosition.labelGravity == LabelGravity.end) {
        secondLabelWidth = 0;
        secondLabelHeight = 0;
      } else {
        secondLabelPainter = TextPainter(
          text: TextSpan(
              text: value.secondLabel,
              style: TextStyle(
                  color: value.secondLabelColor,
                  fontSize: value.secondLabelSize)),
          maxLines: 1,
          textAlign: TextAlign.center,
          textDirection: TextDirection.ltr,
        )..layout();

        secondLabelWidth = secondLabelPainter.width;
        secondLabelHeight = secondLabelPainter.height;
      }

      // 两个标签中的最大值
      var maxWidth = max(labelWidth, secondLabelWidth);
      if (maxWidth < indicatorLine.minLength) {
        maxWidth = indicatorLine.minLength;
      } else if (indicatorLine.maxLength != null &&
          maxWidth > indicatorLine.maxLength!) {
        maxWidth = indicatorLine.maxLength!;
      }

      // 为了确保文本不会与饼状图重叠，计算在饼状图半径基础上的补充半径
      double addedRadius;
      switch (labelPosition.labelGravity) {
        case LabelGravity.end:
          addedRadius = labelHeight / 2;
          break;
        case LabelGravity.top:
          addedRadius = y1 < center.dy ? secondLabelHeight : labelHeight;
          break;
        default:
          addedRadius = y1 < center.dy ? labelHeight : secondLabelHeight;
          break;
      }

      double minLength = 6;
      // 指示线第一段最小长度为 6
      double minOneLine = addedRadius * sin(centerRadian).abs();
      if (minOneLine < minLength) {
        radius2 += minLength;
      } else {
        radius2 += minOneLine;
      }

      // 标签水平方向与指示线间距
      double labelHorizontalOffset = minLength;
      double x2 = center.dx + cos(centerRadian) * radius2;
      double y2 = center.dy + sin(centerRadian) * radius2;
      double x3 = x2;
      if (x2 >= center.dx) {
        if (labelPosition.labelGravity == LabelGravity.end) {
          // 10px为绘制在指示线尾部时，第二根线条默认长度
          x3 += minLength;
        } else {
          x3 += labelHorizontalOffset + maxWidth;
        }
      } else if (labelPosition.labelGravity == LabelGravity.end) {
        x3 += -minLength;
      } else {
        x3 += -labelHorizontalOffset - maxWidth;
      }

      Path tempPath = Path();
      tempPath.moveTo(x1, y1);
      tempPath.lineTo(x2, y2);
      tempPath.lineTo(x3, y2);
      canvas.drawPath(
          tempPath,
          Paint()
            ..style = PaintingStyle.stroke
            ..strokeWidth = indicatorLine.width
            ..color = indicatorLine.color);

      if (labelPainter != null) {
        switch (labelPosition.labelGravity) {
          case LabelGravity.end:
            Offset offset;
            if (x3 - x2 > 0) {
              offset = Offset(x2 + labelHorizontalOffset, y2 - labelHeight / 2);
            } else {
              offset = Offset(
                  x2 - maxWidth - labelHorizontalOffset, y2 - labelHeight / 2);
            }
            labelPainter.paint(canvas, offset);
            break;
          case LabelGravity.top:
            Offset offset;
            if (x3 - x2 > 0) {
              offset = Offset(
                  x2 + labelHorizontalOffset + (maxWidth - labelWidth),
                  y2 - labelHeight - indicatorLine.width);
            } else {
              offset = Offset(x2 - maxWidth - labelHorizontalOffset,
                  y2 - labelHeight - indicatorLine.width);
            }
            labelPainter.paint(canvas, offset);
            break;
          default:
            Offset offset;
            if (x3 - x2 > 0) {
              offset = Offset(
                  x2 + labelHorizontalOffset + (maxWidth - labelWidth),
                  y2 + indicatorLine.width);
            } else {
              offset = Offset(x2 - maxWidth - labelHorizontalOffset,
                  y2 + indicatorLine.width);
            }
            labelPainter.paint(canvas, offset);
            break;
        }

        if (secondLabelPainter != null) {
          switch (labelPosition.labelGravity) {
            case LabelGravity.bottom:
              Offset offset;
              if (x3 - x2 > 0) {
                offset = Offset(
                    x2 + labelHorizontalOffset + (maxWidth - secondLabelWidth),
                    y2 - secondLabelHeight - indicatorLine.width);
              } else {
                offset = Offset(x2 - maxWidth - labelHorizontalOffset,
                    y2 - secondLabelHeight - indicatorLine.width);
              }
              secondLabelPainter.paint(canvas, offset);
              break;
            case LabelGravity.top:
              Offset offset;
              if (x3 - x2 > 0) {
                offset = Offset(
                    x2 + labelHorizontalOffset + (maxWidth - secondLabelWidth),
                    y2 + indicatorLine.width);
              } else {
                offset = Offset(x2 - maxWidth - labelHorizontalOffset,
                    y2 + indicatorLine.width);
              }
              secondLabelPainter.paint(canvas, offset);
              break;
            default:
              break;
          }
        }
      }
    }
  }
}

/// 饼状图数据
class PieChartEntity extends BasePieChartEntity {
  PieChartEntity(
      {required super.color,
      super.drawLabel = false,
      super.label,
      super.labelSize,
      super.labelColor,
      super.addedRadius = 0,
      super.value,
      super.isDraw,
      super.data,
      this.secondLabel,
      this.secondLabelSize = 10,
      this.secondLabelColor = Colors.black});

  /// 第二个标签
  ///
  /// 仅当[KqPieChartDelegate.labelPosition] == [OutSide]并且[OutSide.labelGravity] != [LabelGravity.end]时才绘制。
  /// 其绘制位置与[label]相反，如[label]绘制在[LabelGravity.top],则此标签绘制在[LabelGravity.bottom]位置，反之亦然
  final String? secondLabel;

  /// 标签字体大小
  final double secondLabelSize;

  /// 标签字体颜色
  final Color secondLabelColor;
}

/// [KqPieChartDelegate]手势处理
class PieChartGestureHandler
    with BaseChartGestureHandlerMixin<KqPieChartDelegate> {

  /// 构建手势处理器
  ///
  /// [touchData]为当前选中数据，主动赋值相当于模拟用户点击
  PieChartGestureHandler(
      {this.tapEnable = true, this.dragEnable = true, this.onTap,PieChartEntity? touchData}) {
    _touchData = touchData;
  }

  @override
  final bool tapEnable;

  /// 是否支持旋转
  @override
  final bool dragEnable;

  /// 点击监听，回调参数为空说明点击了非特定数据区域，不为空则为某个数据的绘制区域
  final PieChartTapCallback? onTap;

  double _rotation = 0;
  double? _firstMoveAngle;
  PieChartEntity? _touchData;
  PieChartEntity? _oldTouchData;
  AnimationController? _animationController;

  /// 旋转方向，顺时针还是逆时针,最多取样5份
  List<int> _rotationDirection = [];
  KqPieChartDelegate? _chart;

  @override
  void attachChart(KqPieChartDelegate delegate) {
    _animationController?.dispose();
    _chart = delegate;
  }

  @override
  void dispose() {
    _animationController?.dispose();
    _chart = null;
  }

  @override
  void onTapDown(DragDownDetails details) {
    if (_chart == null || (!tapEnable && !dragEnable)) {
      return;
    }

    if (_animationController != null && _animationController!.isAnimating) {
      _animationController?.stop(canceled: true);
    }

    _firstMoveAngle = null;
    _rotationDirection.clear();
    _oldTouchData = findClickData(details);
  }

  @override
  void onTapUp(DragDownDetails details) {
    if (_chart == null || !tapEnable) {
      return;
    }

    // 单次点击
    if (_touchData != _oldTouchData) {
      _touchData = _oldTouchData;
      _chart?.update();
      onTap?.call(_touchData);
    } else if (touchData != null) {
      _touchData = null;
      _chart?.update();
      onTap?.call(_oldTouchData);
    } else {
      onTap?.call(null);
    }
  }

  @override
  void onDragStart(DragStartDetails details) {}

  @override
  void onDragUpdate(DragUpdateDetails details) {
    if (_chart == null || !dragEnable) {
      return;
    }

    double angle =
        getAngleForPoint(details.localPosition.dx, details.localPosition.dy);
    if (_firstMoveAngle == null) {
      _firstMoveAngle = angle;
    } else {
      var sweepAngle = angle - (_firstMoveAngle ?? 0);
      if (sweepAngle != 0) {
        var direction = sweepAngle > 0 ? 1 : -1;
        if (_rotationDirection.length >= 5) {
          _rotationDirection.removeAt(0);
        }
        _rotationDirection.add(direction);
      }

      _rotation += sweepAngle;
      _rotation = _rotation.to360Range;
      _firstMoveAngle = angle;
      _chart?.update();
    }
  }

  @override
  void onDragEnd(DragEndDetails details) {
    if (_chart == null || !dragEnable) {
      return;
    }

    flingRotation(details);
  }

  /// 执行手势旋转动画
  @protected
  void flingRotation(DragEndDetails details) {
    var velocity = details.velocity.pixelsPerSecond;
    if (velocity.dx.abs() <= 50 && velocity.dy.abs() <= 50) {
      return;
    }

    KqPieChartDelegate? chart = _chart;
    if (chart == null) {
      return;
    }

    var state = chart.chartState;
    if (state == null) {
      return;
    }

    var direction = _rotationDirection;
    if (direction.isEmpty) {
      return;
    }

    double angleStep;
    double maxAngle = 8;
    int duration;
    if (velocity.dx.abs() > velocity.dy.abs()) {
      duration = velocity.dx.abs().toInt();
    } else {
      duration = velocity.dy.abs().toInt();
    }

    var clockwiseCount = 0;
    var counterclockwiseCount = 0;
    for (var value in _rotationDirection) {
      if (value > 0) {
        clockwiseCount++;
      } else {
        counterclockwiseCount++;
      }
    }

    angleStep = maxAngle * (clockwiseCount > counterclockwiseCount ? 1 : -1);

    if (_animationController?.isAnimating == true) {
      _animationController?.stop(canceled: true);
    }

    TickerProviderStateMixin;
    _animationController = AnimationController(
        vsync: state.vsync, duration: Duration(milliseconds: duration));
    var curvedAnimation = CurvedAnimation(
        parent: _animationController!, curve: Curves.decelerate);
    curvedAnimation.addListener(() {
      _rotation += angleStep * (1 - _animationController!.value);
      _rotation = rotation.to360Range;
      state.update();
    });
    _animationController?.forward();
  }

  /// 查找点击位置对应的饼状图数据
  @protected
  PieChartEntity? findClickData(DragDownDetails details) {
    KqPieChartDelegate? chart = _chart;
    if (chart == null) {
      return null;
    }

    var dataList = chart.data;
    if (dataList == null || dataList.isEmpty) {
      return null;
    }

    double angleForPoint =
        getAngleForPoint(details.localPosition.dx, details.localPosition.dy);
    PieChartEntity? entity;
    for (var data in dataList) {
      double startAngle = data.drawStartAngle.to360Range;
      if (startAngle + data.drawSweepAngle > 360) {
        if (angleForPoint >= startAngle && angleForPoint < 360) {
          entity = data;
          break;
        } else if (angleForPoint >= 0 &&
            angleForPoint <= data.drawSweepAngle - (360 - startAngle)) {
          entity = data;
          break;
        }
      } else if (angleForPoint >= startAngle &&
          angleForPoint <= startAngle + data.drawSweepAngle) {
        entity = data;
        break;
      }
    }

    if (entity == null) {
      return null;
    }

    Offset center = chart.size.center(Offset.zero);
    double tx = details.localPosition.dx - center.dx;
    double ty = details.localPosition.dy - center.dy;
    double length = sqrt(tx * tx + ty * ty);
    if (length < chart.innerRadius ||
        length > chart.radius + entity.addedRadius) {
      return null;
    }

    return entity;
  }

  /// 获取触摸点相对于圆心的旋转角度
  @protected
  double getAngleForPoint(double x, double y) {
    var offset = _chart?.size.center(Offset.zero);
    if (offset == null) {
      return 0;
    }

    double tx = x - offset.dx;
    double ty = y - offset.dy;
    double length = sqrt(tx * tx + ty * ty);
    double r = acos(tx / length);
    var angle = r.toDegrees;
    if (y < offset.dy) angle = 360 - angle;
    return angle % 360;
  }

  /// 获取手势旋转角度
  double get rotation => _rotation;

  /// 返回点击区域对应的饼状图数据
  PieChartEntity? get touchData => _touchData;
}

/// 点击监听，回调参数为空说明点击了非特定数据区域，不为空则为某个数据的绘制区域
typedef PieChartTapCallback = Function(PieChartEntity? entity);

/// 饼状图高亮数据绘制器
class PieChartHighLightRender {
  const PieChartHighLightRender(
      {this.colorBuilder, this.addedRadius = 0, this.offsetRadius = 10})
      : assert(addedRadius >= 0),
        assert(offsetRadius >= 0);

  /// 高亮颜色构建器
  final Color Function(PieChartEntity)? colorBuilder;

  /// 高亮时半径增加的长度，中心点不发生偏移
  final double addedRadius;

  /// 高亮时中心点向外移动的半径长度，不增加绘制的半径长度
  final double offsetRadius;

  /// 绘制高亮数据
  void onDraw(KqPieChartDelegate chart, Canvas canvas, double drawStartAngle,
      double drawSweepAngle, PieChartEntity data) {
    Offset center = chart.size.center(Offset.zero);
    double radius = getRadius(chart.radius, data);
    Offset offset = getOffset(data);
    Color color = colorBuilder?.call(data) ?? data.color;
    canvas.drawArcEx(
        center: center,
        radius: radius,
        innerRadius: chart.innerRadius,
        startAngle: drawStartAngle,
        sweepAngle: drawSweepAngle,
        color: color,
        offset: offset,
        sliceSpace: chart.sliceSpace);
  }

  /// 返回高亮绘制内容的半径
  double getRadius(double radius, PieChartEntity data) {
    return radius + data.addedRadius + addedRadius;
  }

  /// 获取X轴，Y轴偏移值
  Offset getOffset(PieChartEntity data) {
    if (offsetRadius <= 0) {
      return Offset.zero;
    }

    double centerRadian =
        (data.drawStartAngle + data.drawSweepAngle / 2).toRadian;
    return Offset(
        offsetRadius * cos(centerRadian), offsetRadius * sin(centerRadian));
  }
}
